package com.yanqu.road.server.manager.cross.battle;

import com.alibaba.fastjson.JSONArray;
import com.yanqu.road.entity.activity.ActivityConfig;
import com.yanqu.road.entity.activity.ActivityInfo;
import com.yanqu.road.entity.activity.shuihu.ShuiHuBox;
import com.yanqu.road.entity.activity.shuihu.ShuiHuBoxItem;
import com.yanqu.road.entity.activity.shuihu.ShuiHuNotice;
import com.yanqu.road.entity.activity.shuihu.config.ShuiHuCardConfig;
import com.yanqu.road.entity.activity.shuihu.config.ShuiHuConfig;
import com.yanqu.road.entity.enums.eActivityConfigType;
import com.yanqu.road.entity.player.UserBaseInfo;
import com.yanqu.road.logic.bussiness.activity.ShuiHuActivityBussiness;
import com.yanqu.road.logic.bussiness.servercenter.ActivityBussiness;
import com.yanqu.road.logic.pb.ShuiHuPb;
import com.yanqu.road.utils.YanQuMessageUtils;
import com.yanqu.road.pb.activity.ShuiHuActivityProto;
import com.yanqu.road.server.logic.MessageHelper;
import com.yanqu.road.server.protocol.ClientProtocol;
import com.yanqu.road.server.protocol.GameErrorCode;
import com.yanqu.road.server.protocol.GameProtocol;
import com.yanqu.road.utils.RandomHelper;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

public class ShuiHuActivity {

    private final Logger logger = LogManager.getLogger(ShuiHuActivity.class);

    private final Object lockObject = new Object();

    private ShuiHuConfig shuiHuConfig;

    private int maxRoundNo;

    // Map<第几轮，第几格，数据>
    private Map<Integer, Map<Integer, ShuiHuBoxItem>> shuiHuBoxMap;

    private List<ShuiHuNotice> shuiHuNoticeList;

    // 待同步消息
    private List<ShuiHuActivityProto.DrawCardDataMsg> toSyncList = new ArrayList<>();
    // 同步消息专用锁
    private final Object syncLockObject = new Object();

    public ShuiHuActivity(ActivityInfo activityInfo) {
        // 加载配置
        reloadConfig(activityInfo);

        // 加载数据
        shuiHuBoxMap = new ConcurrentHashMap<>();
        Integer tempRoundNo = ShuiHuActivityBussiness.getShuiHuBoxMaxRoundNo(activityInfo.getActivityId());
        if (tempRoundNo != null && tempRoundNo > 0) {
            maxRoundNo = tempRoundNo;
            shuiHuBoxMap.put(maxRoundNo, ShuiHuActivityBussiness.getShuiHuBoxItemMap(activityInfo.getActivityId(), maxRoundNo));
        } else {
            maxRoundNo = 0;
            initNewShuiHuBox(1);
        }
        shuiHuNoticeList = ShuiHuActivityBussiness.getShuiHuNoticeList(activityInfo.getActivityId());

        // 判断是否要新开一轮
        if (isNeedNewRound(shuiHuBoxMap.get(maxRoundNo))) {
            initNewShuiHuBox(maxRoundNo + 1);
        }
    }

    public void save() {
        List<Integer> removeRoundNoList = new ArrayList<>();
        for (Map.Entry<Integer, Map<Integer, ShuiHuBoxItem>> mapEntry : shuiHuBoxMap.entrySet()) {
            Map<Integer, ShuiHuBoxItem> tempBox = mapEntry.getValue();
            for (ShuiHuBoxItem tempItem : tempBox.values()) {
                if (tempItem.isInsertOption()) {
                    ShuiHuActivityBussiness.addShuiHuBoxItem(tempItem);
                } else if (tempItem.isUpdateOption()) {
                    ShuiHuActivityBussiness.updateShuiHuBoxItem(tempItem);
                }
            }
            // 旧数据没用了，内存里就留2份
            if (mapEntry.getKey() < maxRoundNo - 2) {
                removeRoundNoList.add(mapEntry.getKey());
            }
        }
        for (int removeRoundNo : removeRoundNoList) {
            shuiHuBoxMap.remove(removeRoundNo);
        }

        for (int i = 0; i < shuiHuNoticeList.size(); i++) {
            ShuiHuNotice notice = shuiHuNoticeList.get(i);
            if (notice.isInsertOption()) {
                ShuiHuActivityBussiness.addShuiHuNotice(notice); // 没有更新操作
            }
        }
    }

    public void reloadConfig(ActivityInfo activityInfo) {
        logger.info("ShuiHu Activity reload config start!");

        int activityId = activityInfo.getActivityId();
        List<Integer> activityIdList = new ArrayList<>();
        activityIdList.add(activityId);

        ShuiHuConfig tmpConfig = new ShuiHuConfig();
        tmpConfig.setActivityId(activityId);
        tmpConfig.setActivityInfo(activityInfo);
        // 卡片
        tmpConfig.setCardConfigMap(ShuiHuActivityBussiness.getShuiHuCardConfigMap(activityInfo.getActivityId()));
        // 组合
        tmpConfig.setCombConfigMap(ShuiHuActivityBussiness.getShuiHuCombConfigMap(activityInfo.getActivityId()));
        // 其他配置
        Map<String, ActivityConfig> activityConfigMap = ActivityBussiness.getActivityConfigMap(activityIdList).get(activityId);
        tmpConfig.setDrawCost(activityConfigMap.get(eActivityConfigType.SHUIHU_DRAW_COST.getName()).getValue());
        tmpConfig.setNoticeMaxCount(activityConfigMap.get(eActivityConfigType.SHUIHU_NOTICE_MAX_COUNT.getName()).getIntValue());
        tmpConfig.setQualityCountList(activityConfigMap.get(eActivityConfigType.SHUIHU_QUALITY_COUNT.getName()).getIntListValue("\\|"));
        tmpConfig.setBigRewardCardId(activityConfigMap.get(eActivityConfigType.SHUIHU_BIG_REWARD_CARD_ID.getName()).getIntValue());
        tmpConfig.setSpecialQualityList(activityConfigMap.get(eActivityConfigType.SHUIHU_SPECIAL_QUALITY.getName()).getValue());

        // 校验下是否有正确配置大奖卡
        int bigCardId = tmpConfig.getBigRewardCardId();
        int bigQuality = tmpConfig.getCardConfigMap().get(bigCardId).getQuality();
        int bigQualityCount = 0;
        for (ShuiHuCardConfig cardConfig : tmpConfig.getCardConfigMap().values()) {
            if (cardConfig.getQuality() == bigQuality) {
                bigQualityCount++;
            }
        }
        if (bigQualityCount != 1) {
            logger.error("ShuiHu Activity config error! BigRewardCard's quality contains more than one CardConfig!");
            return;
        }

        shuiHuConfig = tmpConfig;
        logger.info("ShuiHu Activity reload config finish!");
    }

    /**
     * 预留接口
     */
    public void repair() {

    }

    /**
     * 初始化第N轮奖池
     */
    private void initNewShuiHuBox(int roundNo) {
        if (maxRoundNo + 1 != roundNo) {
            return;
        }
        synchronized (lockObject) {
            if (maxRoundNo + 1 != roundNo) {
                return;
            }
            shuiHuBoxMap.put(roundNo, buildShuiHuBoxMap(roundNo));
            maxRoundNo++;
        }
    }

    /**
     * 创建一批奖池奖励
     */
    private Map<Integer, ShuiHuBoxItem> buildShuiHuBoxMap(int roundNo) {
        // 把卡片按品质分类
        Map<Integer, List<ShuiHuCardConfig>> cardConfigListMap = new ConcurrentHashMap<>();
        for (ShuiHuCardConfig config : shuiHuConfig.getCardConfigMap().values()) {
            if (!cardConfigListMap.containsKey(config.getQuality())) {
                cardConfigListMap.put(config.getQuality(), new ArrayList<>());
            }
            cardConfigListMap.get(config.getQuality()).add(config);
        }

        // 按品质创建
        Random random = new Random();
        List<Integer> cardIdList = new ArrayList<>();
        List<Integer> qualityCountList = shuiHuConfig.getQualityCountList();
        for (int quality = 1; quality <= qualityCountList.size(); quality++) {
            if (!cardConfigListMap.containsKey(quality)) {
                logger.error("ShuiHu qualityCountList error, value={}", JSONArray.toJSONString(shuiHuConfig.getQualityCountList()));
                break;
            }
            // 数量
            int count = qualityCountList.get(quality - 1); // 下标 + 1 = 品质
            // 按权重随机卡片
            List<ShuiHuCardConfig> cardConfigList = cardConfigListMap.get(quality);
            List<Integer> weightList = cardConfigList.stream().mapToInt(ShuiHuCardConfig::getWeight).boxed().collect(Collectors.toList());
            for (int i = 0; i < count; i++) {
                int index = RandomHelper.getRandomIndexByWeight(weightList, random);
                cardIdList.add(cardConfigList.get(index).getId());
            }
        }

        // 乱序
        Collections.shuffle(cardIdList, random);

        // 组装结果
        Map<Integer, ShuiHuBoxItem> resultMap = new ConcurrentHashMap<>();
        int activityId = shuiHuConfig.getActivityId();
        for (int i = 0; i < cardIdList.size(); i++) {
            ShuiHuBoxItem item = new ShuiHuBoxItem();
            item.setActivityId(activityId);
            item.setRoundNo(roundNo);
            item.setItemNo(i);
            item.setCardId(cardIdList.get(i));
            item.setUserId(0);
            item.setUserBaseInfo(new UserBaseInfo());
            item.setInsertOption();
            resultMap.put(i, item);
        }
        return resultMap;
    }

    /**
     * 获取奖池数据
     */
    public ShuiHuBox getShuiHuBox() {
        return new ShuiHuBox(maxRoundNo, shuiHuBoxMap.get(maxRoundNo));
    }

    /**
     * 获取公告
     */
    public List<ShuiHuNotice> getShuiHuNoticeList() {
        return shuiHuNoticeList;
    }

    /**
     * 获取配置
     */
    public ShuiHuConfig getShuiHuConfig() {
        return shuiHuConfig;
    }

    /**
     * 抽卡
     *
     * @return List：0错误码，卡片ID
     */
    public List<Integer> drawCard(int itemNo, int roundNo, long userId, UserBaseInfo userBaseInfo) {

        List<Integer> result = new ArrayList<>();
        result.add(0);
        result.add(0);

        int bigRewardCardId = shuiHuConfig.getBigRewardCardId();

        boolean needNotice = false;

        ShuiHuBoxItem boxItem;

        synchronized (lockObject) {
            // 校验第几轮
            if (maxRoundNo != roundNo) {
                result.set(0, (int) GameErrorCode.E_SHUIHU_ACTIVITY_BOX_REFRESH);
                return result;
            }
            // 校验格子是否开启
            Map<Integer, ShuiHuBoxItem> boxItemMap = shuiHuBoxMap.get(maxRoundNo);
            boxItem = boxItemMap.get(itemNo);
            if (isCardOpened(boxItem)) {
                result.set(0, (int) GameErrorCode.E_SHUIHU_ACTIVITY_BOX_ITEM_HAD_OPEN);
                return result;
            }

            // 开启
            boxItem.setUserId(userId);
            boxItem.setUserBaseInfo(userBaseInfo);
            result.set(1, boxItem.getCardId());

            // 抽到大奖
            if (boxItem.getCardId() == bigRewardCardId) {
                // 校验是否要下一轮
                if (isNeedNewRound(boxItemMap)) {
                    initNewShuiHuBox(roundNo + 1);
                }
                // 上公告
                needNotice = true;
            }

            // 添加消息（sync内添加，保证有序）
            addDrawMsg(boxItem);
        }

        // 大奖要上公告
        if (needNotice) {
            ShuiHuNotice shuiHuNotice = createShuiHuNotice(result.get(1), roundNo, userId, userBaseInfo);
            shuiHuNoticeList.add(shuiHuNotice);
        }

        return result;
    }

    /**
     * 新建公告
     */
    private ShuiHuNotice createShuiHuNotice(int cardId, int roundNo, long userId, UserBaseInfo userBaseInfo) {
        ShuiHuNotice notice = new ShuiHuNotice();
        notice.setActivityId(shuiHuConfig.getActivityId());
        notice.setRoundNo(roundNo);
        notice.setUserId(userId);
        notice.setUserBaseInfo(userBaseInfo);
        notice.setCardId(cardId);
        notice.setCreateTime(System.currentTimeMillis());
        notice.setInsertOption();
        return notice;
    }

    /**
     * 获取开启者信息
     */
    public int getOpenerData(long serverId, long userId, int roundNo, int itemNo) {
        Map<Integer, ShuiHuBoxItem> shuiHuBox = shuiHuBoxMap.get(roundNo);
        if (shuiHuBox == null) {
            return GameErrorCode.E_SHUIHU_ACTIVITY_BOX_REFRESH;
        }

        ShuiHuBoxItem boxItem = shuiHuBox.get(itemNo);
        if (!isCardOpened(boxItem)) {
            return GameErrorCode.E_SHUIHU_ACTIVITY_BOX_ITEM_NO_OPEN;
        }

        ShuiHuActivityProto.GetOpenerRespMsg.Builder builder = ShuiHuActivityProto.GetOpenerRespMsg.newBuilder();
        builder.setRet(0);
        builder.setNickName(boxItem.getUserBaseInfo().getNickName());
        builder.setServerId(boxItem.getUserBaseInfo().getServerId());
        MessageHelper.sendPacket(serverId, userId, YanQuMessageUtils.buildMessage(ClientProtocol.U_SHUIHU_ACTIVITY_GET_OPENER, builder));
        return 0;
    }

    /**
     * 卡片是否已翻开
     */
    private boolean isCardOpened(ShuiHuBoxItem boxItem) {
        return boxItem.getUserId() != 0;
    }

    /**
     * 是否需要新开一轮
     */
    private boolean isNeedNewRound(Map<Integer, ShuiHuBoxItem> boxItemMap) {
        for (ShuiHuBoxItem tmpBoxItem : boxItemMap.values()) {
            if (tmpBoxItem.getCardId() == shuiHuConfig.getBigRewardCardId() && !isCardOpened(tmpBoxItem)) {
                return false;
            }
        }
        return true;
    }

    /**
     * 添加抽卡消息
     */
    public void addDrawMsg(ShuiHuBoxItem boxItem) {
        ShuiHuActivityProto.DrawCardDataMsg.Builder builder = ShuiHuActivityProto.DrawCardDataMsg.newBuilder();
        builder.setRoundNo(boxItem.getRoundNo());
        builder.setItemMsg(ShuiHuPb.parseShuiHuBoxItemMsg(boxItem));
        synchronized (syncLockObject) {
            toSyncList.add(builder.build());
        }
    }

    /**
     * 同步抽卡消息
     */
    public void syncDrawMsgList(boolean checkSize) {
        List<ShuiHuActivityProto.DrawCardDataMsg> list;
        synchronized (syncLockObject) {

            if (checkSize) {
                if (!needAdvanceSyncDraw()) {
                    return;
                }
            }

            list = new ArrayList<>(toSyncList);
            toSyncList.clear();
        }
        if (list.size() > 0) {
            ShuiHuActivityProto.SyncDrawCardDataRespMsg.Builder builder = ShuiHuActivityProto.SyncDrawCardDataRespMsg.newBuilder();
            builder.addAllDrawCardMsgList(list);
            ShuiHuActivityProto.SyncDrawCardDataRespMsg respMsg = builder.build();
            List<Long> serverIdList = shuiHuConfig.getActivityInfo().getServerIdList();
            for (long serverId : serverIdList) {
                MessageHelper.sendPacket(serverId, 0, YanQuMessageUtils.buildMessage(GameProtocol.S_SHUIHU_ACTIVITY_SYNC_DRAW_CARD, respMsg));
            }
        }
    }

    /**
     * 是否需要提前同步
     */
    public boolean needAdvanceSyncDraw() {
        return toSyncList.size() > 30;
    }

}